<template>
  <div class="_numb">
    <span v-if="num != undefined" v-text="num"></span>
    <span v-if="to>rate&&num>=rate" class="_ads">+</span>
  </div>
</template>
<script>
import { reactive, ref, computed, onMounted, watch, nextTick } from 'vue'
import { tween } from 'shifty'

const MANUAL = 'manual'
const AUTO = 'auto'
const RE_FLOAT = /^\d+(\.\d+)?$/
const RE_INT = /^\[1-9][0-9]*$/
const RE_COLOR = /^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/
const isNumber = (v) => {
  return typeof v === 'number' || (
    typeof v === 'string' && (RE_FLOAT.test(v) || RE_INT.test(v))
  )
}
const isColor = (v) => {
  return RE_COLOR.test(v)
}

const extend = (target, source) => {
  for (let k in source) {
    target[k] = source[k]
  }
}
const hexToRGB = (hex) => {
  hex = hex.replace('#', '')
  if (hex.length === 3) {
    hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`
  }

  return {
    r: parseInt(hex.substr(0, 2), 16),
    g: parseInt(hex.substr(2, 2), 16),
    b: parseInt(hex.substr(4, 2), 16)
  }
}

export default {
  name:'FNumberScroll',
  props: {
    from:{ // 期望数字
      type: [Number,String],
      required: true,
      validator: isNumber
    },
    to:{ // 期望数字
      type: [Number,String],
      required: true,
      validator: isNumber
    },
    rate:{  // 目标进度
      type: Number,
      default: 100
    },
    duration: {
      type: [Number, String],
      default: 2000,
      validator: isNumber
    },
    formatter: {
      type: Function,
      default: parseInt
    },
    animateEnd: Function,
    mode: {
      type: String,
      validator: function (v) {
        return v === AUTO || v === MANUAL
      },
      default: AUTO
    },
    easing: {
      type: String,
      default: 'easeOutQuart'
    },
  },
  setup(props){
    const _from = computed(()=>props.from)
    const _to = computed(()=>props.to)
    const _formatter = computed(()=>props.formatter)

    const num = ref(0)
    const options = reactive({easing:props.easing})
    const state = ref(0)

    const updateNumber = (data) => {
      const { state } = data
      const xx = !!state ? state : data
      num.value = _formatter.value
        ? _formatter.value(xx.x)
        : xx.x
    }

    const makeOptions = () => {
      const from = typeof _from.value === 'string'
        ? { x: parseFloat(_from.value) }
        : { x: _from.value }

      const to = typeof _to.value === 'string'
        ? { x: parseFloat(_to.value) }
        : { x: _to.value }

      const duration = typeof props.duration === 'string'
        ? parseFloat(props.duration)
        : props.duration

      const step = updateNumber

      Object.assign(options, { from, to, duration, step })
    }
    
    const start = (ops) => {
      if (state.value > 0) return
      state.value = 1

      if (ops) extend(options, ops)
      if (options.duration === 0) options.duration = 1

      tween(options).then(updateNumber).then(() => {
        state.value = 0
        if (props.animateEnd) props.animateEnd(parseFloat(num.value))
      })
    }

    watch(()=>props.to, (_new)=>{
      makeOptions()
      if (props.mode === AUTO) start()
      if (props.mode === MANUAL) {
        typeof _from.value === 'string'
          ? num.value = _formatter.value(parseFloat(_from.value))
          : num.value = _formatter.value(_from.value)
      }
    },{immediate:true})

    return{
      num
    }
  }
}
</script>
<style lang="less" scoped>
._numb{
  display: flex;
  justify-content: center;
  align-items: center;
  ._ads{
    font-size: 20px;
    margin-left: 5px;
  }
}
</style>